#pragma once

#include "logger.hpp"
#include "yaml/include/yaml-cpp/yaml.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>

struct Point_t;

using PointTable_t = std::map<std::string, Point_t>;

struct Point_t {
    uint16_t addr;              /* 点地址 */
    float unit;                 /* 单位 */
    std::string type;           /* 数据类型 */
    uint8_t readFunc;           /* 读功能码 */
    uint8_t writeFunc;          /* 写功能码 */
    uint16_t len;               /* 寄存器长度 */
    uint32_t rValue;            /* 读到的缓存值 */
    uint32_t rawValue;          /* 寄存器原始值 */
    std::string rString;        /* 读到的是字符串 */
    std::pair<int, int> bitMap; /* 位域 */
    PointTable_t pointTable;    /* 内嵌点表,双层结构 */
    bool operaResult;           /* 保存上一次的操作结果 */
    uint32_t wValue;            /* 保存写入的值 */

public:
    Point_t()
    {
        rawValue = wValue = readFunc = writeFunc = len = addr = rValue = 0;
        unit = 1.0; /* 默认为1 */
        bitMap = std::make_pair(-1, -1);
        operaResult = false;
    }

    void operaSuccess(void)
    {
        operaResult = true;
    }

    void operaFail(void)
    {
        operaResult = false;
    }

    bool operationResult(void)
    {
        return operaResult;
    }

    // bool isBitMapPoint(void)
    // {
    //     return (bitMap.first == bitMap.second == -1);
    // }
};

class Interface : public Logger {
protected:
    PointTable_t PointTable;

private:
    bool parsePoint(const YAML::detail::iterator_value &point, PointTable_t &PointTable);

public:
    Interface() = default;

    virtual bool readPoint(const std::string &pointName) = 0;
    virtual bool writePoint(const std::string &pointName, uint32_t value) = 0;

    virtual std::vector<uint8_t> read(void) = 0;
    virtual bool write(const std::vector<uint8_t> &) = 0;

    virtual std::vector<uint8_t> transmit(const std::vector<uint8_t> &) = 0;

    bool initPointTable(const std::string &profile);

    int getBitsValue(int value, int startBits, int endBits);

    uint32_t getMask(int startBits, int endBits);

    std::vector<std::pair<const std::string, Point_t> *> findPointByName(const std::string &pointName);

    std::pair<const std::string, Point_t> *getFirstPoint(void);

    virtual ~Interface() {};
};

bool Interface::initPointTable(const std::string &profile)
{
    if (!std::ifstream(profile).good()) {
        error("profile not exist : {}", profile);
        return false;
    }

    PointTable.clear();

    YAML::Node root = YAML::LoadFile(profile);
    if (!root["resources"].IsDefined()) {
        error("can't get resources\n");
        return false;
    }
    information("get resource\n");

    if (!root["resources"].IsSequence()) {
        error("resources not is sequence\n");
        return false;
    }

    information("resources is sequence\n");

    for (const auto &item : root["resources"]) {
        parsePoint(item, PointTable);
    }

    return true;
}

bool Interface::parsePoint(const YAML::detail::iterator_value &point, PointTable_t &PointTable)
{
    std::string pointName;
    Point_t p;

    if (point["name"].IsDefined()) {
        pointName = point["name"].as<std::string>();
    } else {
        throw std::runtime_error("point not have name");
        return false; /* 一个点不能没有名字 */
    }

    if (point["type"].IsDefined()) {
        p.type = point["type"].as<std::string>();

        if (p.type == "class") {
            if (point["subItems"].IsDefined()) {
                if (point["subItems"].IsSequence()) {
                    for (const auto &item : point["subItems"]) {
                        parsePoint(item, p.pointTable);
                    }
                }
            }
        }
    } else {
        throw std::runtime_error("point not have type");
        return false; /* 一个点不能没有type */
    }

    if (point["len"].IsDefined()) {
        p.len = point["len"].as<int>();
    }

    if (point["addr"].IsDefined()) {
        p.addr = point["addr"].as<int>();
    }

    if (point["bitMap"].IsDefined()) {
        if (point["bitMap"].IsSequence()) {
            if (point["bitMap"].size() == 2) {
                p.bitMap = std::make_pair(point["bitMap"][0].as<int>(), point["bitMap"][1].as<int>());
            }
        }
    }

    if (point["permi"].IsDefined()) {
        if (point["permi"].IsSequence()) {
            if (point["permi"].size() == 1) {
                if (!point["permi"][0].IsNull()) {
                    p.readFunc = point["permi"][0].as<int>();
                }

            } else {
                if (!point["permi"][0].IsNull()) {
                    p.readFunc = point["permi"][0].as<int>();
                }

                if (!point["permi"][1].IsNull()) {
                    p.writeFunc = point["permi"][1].as<int>();
                }
            }
        }
    }

    if (point["prec"].IsDefined()) {
        p.unit = point["prec"].as<float>();
    }

    PointTable.insert(std::make_pair(pointName, p));
    return true;
}

int Interface::getBitsValue(int value, int startBits, int endBits)
{
    if (endBits < startBits) {
        error("endBits < startBits\n");
        return 0;
    }

    int mask = ((1 << (endBits - startBits + 1)) - 1) << startBits;
    return (value & mask) >> startBits;
}

uint32_t Interface::getMask(int startBits, int endBits)
{
    if (endBits < startBits) {
        error("endBits < startBits\n");
        return 0;
    }

    uint32_t mask = (1u << (endBits + 1)) - 1;
    mask &= ~((1u << startBits) - 1);
    return mask;
}

std::vector<std::pair<const std::string, Point_t> *> Interface::findPointByName(const std::string &pointName)
{
    /* 目前支持2层嵌套 */
    auto point = std::find_if(PointTable.begin(), PointTable.end(), [pointName](const std::pair<const std::string, Point_t> &item) {
        return item.first == pointName || item.second.pointTable.find(pointName) != item.second.pointTable.end();
    });

    std::vector<std::pair<const std::string, Point_t> *> result;

    if (point == PointTable.end()) {
        return result;
    }

    if (point->first == pointName) {
        if (point->second.type == "class") {
            for (auto &p : point->second.pointTable) {
                result.push_back(&p);
            }
        } else {
            result.push_back(&(*point));
        }
    } else {
        auto it = point->second.pointTable.find(pointName);
        if (it == point->second.pointTable.end()) {
            return result;
        }
        result.push_back(&(*it));
    }

    return result;
}

std::pair<const std::string, Point_t> *Interface::getFirstPoint(void)
{
    if (!PointTable.size()) {
        return nullptr;
    }

    for (auto &item : PointTable) {
        if (item.second.readFunc != 0) {
            return &item;
        }
    }

    return nullptr;
}